//go:build darwin

package transport

import (
	"golang.org/x/sys/unix"
)

var WorkerCount = 40

func (w *transportPool) Init() error {
	kqFd, err := unix.Kqueue()
	w.fd = kqFd
	return err
}

func (w *transportPool) Add(t Transport) error {
	fd := t.FD()
	w.transports.Store(fd, t)
	return w.addRead(uint64(fd))
}
func (w *transportPool) addRead(fd uint64) error {

	kev := unix.Kevent_t{
		Ident:  fd,
		Filter: unix.EVFILT_READ,
		Flags:  unix.EV_ADD,
	}
	_, err := unix.Kevent(w.fd, []unix.Kevent_t{kev}, []unix.Kevent_t{}, &unix.Timespec{})
	if err == unix.EINTR {
		err = nil
	}
	return err
}

func (w *transportPool) Remove(t Transport) error {
	w.transports.Delete(t.FD())
	return w.removeRead(uint64(t.FD()))
}
func (w *transportPool) removeRead(fd uint64) error {

	kev := unix.Kevent_t{
		Ident:  fd,
		Filter: unix.EVFILT_READ,
		Flags:  unix.EV_DELETE,
	}
	_, err := unix.Kevent(w.fd, []unix.Kevent_t{kev}, []unix.Kevent_t{}, &unix.Timespec{})
	if err == unix.EINTR {
		err = nil
	}
	return err
}

func (w *transportPool) waitMessage() ([]Transport, []Transport, error) {
	events := make([]unix.Kevent_t, WorkerCount)
	for {
		n, err := unix.Kevent(w.fd, []unix.Kevent_t{}, events, &unix.Timespec{Sec: 1, Nsec: 0})
		if n == -1 {
			if err == unix.EINTR {
				continue
			}
			return nil, nil, err
		}
		if n == 0 {
			// If there are no events, try again.
			continue
		}
		ts := make([]Transport, 0)
		closedTs := make([]Transport, 0)
		for _, event := range events[:n] {
			if t, ok := w.getTransport(int(event.Ident)); ok {
				if event.Data&unix.EVFILT_READ != 0 {
					ts = append(ts, t)
				}
				if event.Data == 0 && event.Flags&unix.EV_EOF != 0 {
					w.closeTransport(t)
					closedTs = append(closedTs, t)
				}
			} else {
				_ = w.removeRead(event.Ident)
			}
		}
		return ts, closedTs, nil
	}
}
